package com.gooluke.mybatis.handler;

import com.gooluke.mybatis.annotation.Param;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;

import javax.sql.DataSource;
import java.lang.reflect.*;
import java.sql.ResultSetMetaData;
import java.sql.Timestamp;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SqlMethodHandler implements MethodHandler {
    private final String sql;
    private final Method method;
    private final DataSource dataSource;
    private final JdbcTemplate jdbcTemplate;

    public SqlMethodHandler(String sql, Method method, DataSource dataSource) {
        this.sql = sql;
        this.method = method;
        this.dataSource = dataSource;
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public Object handle(Object[] args) {
        try {
            // 1. 解析参数名和值
            Map<String, Object> paramMap = buildParamMap(args);

            // 2. 转换为PreparedStatement SQL
            ParsedSql parsedSql = parseSql(sql, paramMap);

            // 3. 执行SQL
            Object o = executeSql(parsedSql, method.getReturnType());
            System.out.println("o.toString() = " + o.toString());
            return o;
        } catch (Exception e) {
            throw new RuntimeException("Failed to execute SQL for method: " + method.getName(), e);
        }
    }

    private Map<String, Object> buildParamMap(Object[] args) {
        Map<String, Object> paramMap = new HashMap<>();
        Parameter[] parameters = method.getParameters();

        for (int i = 0; i < parameters.length; i++) {
            // 使用@Param注解的值或参数名作为key
            Param annotation = parameters[i].getAnnotation(Param.class);
            String paramName = annotation != null ? annotation.value() : parameters[i].getName();
            paramMap.put(paramName, args[i]);

            // 支持通过arg0, arg1,...方式访问
            paramMap.put("arg" + i, args[i]);
        }
        return paramMap;
    }

    private ParsedSql parseSql(String sql, Map<String, Object> paramMap) {
        List<String> paramNames = new ArrayList<>();
        String parsedSql = sql;

        // 匹配 #{param} 格式
        Pattern pattern = Pattern.compile("#\\{(\\w+)\\}");
        Matcher matcher = pattern.matcher(sql);

        while (matcher.find()) {
            String paramName = matcher.group(1);
            paramNames.add(paramName);
        }

        // 替换为?占位符
        parsedSql = parsedSql.replaceAll("#\\{\\w+\\}", "?");

        return new ParsedSql(parsedSql, paramNames, paramMap);
    }

    private Object executeSql(ParsedSql parsedSql, Class<?> returnType) {
        // 准备参数值数组
        Object[] paramValues = new Object[parsedSql.getParamNames().size()];
        for (int i = 0; i < parsedSql.getParamNames().size(); i++) {
            String paramName = parsedSql.getParamNames().get(i);
            paramValues[i] = parsedSql.getParamMap().get(paramName);
        }

        // 判断SQL类型
        String sql = parsedSql.getSql().trim().toUpperCase();

        if (sql.startsWith("SELECT")) {
            return executeQuery(parsedSql.getSql(), paramValues, returnType);
        } else {
            return executeUpdate(parsedSql.getSql(), paramValues, returnType);
        }
    }

    private Object executeQuery(String sql, Object[] args, Class<?> returnType) {
        // 处理集合返回类型
        if (List.class.isAssignableFrom(returnType)) {
            // 获取泛型类型
            Type genericReturnType = method.getGenericReturnType();
            Class<?> elementType = Object.class;

            if (genericReturnType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericReturnType;
                elementType = (Class<?>) pt.getActualTypeArguments()[0];
            }

            // 新方式：使用query()方法并传入参数类型
            return jdbcTemplate.query(sql, newArgPreparedStatementSetter(args), getRowMapper(elementType));
        }
        // 处理单个对象返回
        else if (!returnType.isPrimitive() && returnType != Object.class) {
            // 新方式：使用queryForObject()的新形式
            List<?> results = jdbcTemplate.query(sql, getRowMapper(returnType), args);
            return DataAccessUtils.requiredSingleResult(results);
        }
        // 处理基本类型返回
        else {
            // 新方式：使用queryForObject()的新形式
            return jdbcTemplate.queryForObject(sql, returnType, args);
        }
    }

    // 辅助方法：将参数数组转换为PreparedStatementSetter
    private PreparedStatementSetter newArgPreparedStatementSetter(Object[] args) {
        return ps -> {
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
        };
    }

    private Object executeUpdate(String sql, Object[] args, Class<?> returnType) {
        // 新方式：使用update()方法并传入参数类型
        int affectedRows = jdbcTemplate.update(sql, newArgPreparedStatementSetter(args));

        if (returnType == int.class || returnType == Integer.class) {
            return affectedRows;
        } else if (returnType == boolean.class || returnType == Boolean.class) {
            return affectedRows > 0;
        }
        return null;
    }

    private <T> RowMapper<T> getRowMapper(Class<T> type) {
        return (rs, rowNum) -> {
            try {
                T instance = type.getDeclaredConstructor().newInstance();
                ResultSetMetaData metaData = rs.getMetaData();
                int columnCount = metaData.getColumnCount();

                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnLabel(i);
                    Object value = rs.getObject(i);

                    try {
                        // 尝试直接匹配字段名
                        Field field = type.getDeclaredField(columnName);
                        field.setAccessible(true);
                        setFieldValue(field, instance, value);
                    } catch (NoSuchFieldException e) {
                        // 尝试驼峰命名
                        String camelName = toCamelCase(columnName);
                        try {
                            Field field = type.getDeclaredField(camelName);
                            field.setAccessible(true);
                            setFieldValue(field, instance, value);
                        } catch (NoSuchFieldException ex) {
                            // 忽略不存在的字段
                        }
                    }
                }
                return instance;
            } catch (Exception e) {
                throw new RuntimeException("Failed to map row to " + type.getName(), e);
            }
        };
    }

    // 安全设置字段值
    private void setFieldValue(Field field, Object target, Object value) throws IllegalAccessException {
        if (value == null) {
            field.set(target, null);
            return;
        }

        Class<?> fieldType = field.getType();

        // 处理常见类型转换
        if (fieldType == Integer.class || fieldType == int.class) {
            field.set(target, ((Number) value).intValue());
        } else if (fieldType == Long.class || fieldType == long.class) {
            field.set(target, ((Number) value).longValue());
        } else if (fieldType == Double.class || fieldType == double.class) {
            field.set(target, ((Number) value).doubleValue());
        } else if (fieldType == Float.class || fieldType == float.class) {
            field.set(target, ((Number) value).floatValue());
        } else if (fieldType == Boolean.class || fieldType == boolean.class) {
            field.set(target, convertToBoolean(value));
        } else if (fieldType == String.class) {
            field.set(target, value.toString());
        } else if (fieldType == Date.class && value instanceof Timestamp) {
            field.set(target, new Date(((Timestamp) value).getTime()));
        } else {
            field.set(target, value);
        }
    }

    private boolean convertToBoolean(Object value) {
        if (value instanceof Boolean) {
            return (Boolean) value;
        } else if (value instanceof Number) {
            return ((Number) value).intValue() != 0;
        } else if (value instanceof String) {
            return Boolean.parseBoolean((String) value) || "1".equals(value);
        }
        return false;
    }

    private String toCamelCase(String columnName) {
        StringBuilder result = new StringBuilder();
        boolean nextUpper = false;

        for (int i = 0; i < columnName.length(); i++) {
            char currentChar = columnName.charAt(i);
            if (currentChar == '_') {
                nextUpper = true;
            } else {
                if (nextUpper) {
                    result.append(Character.toUpperCase(currentChar));
                    nextUpper = false;
                } else {
                    result.append(Character.toLowerCase(currentChar));
                }
            }
        }
        return result.toString();
    }
}